package com.obas.validators {
    
    import flash.events.Event;
    import flash.events.IEventDispatcher;
    
    import mx.core.UIComponent;
    import mx.events.FlexEvent;
    import mx.events.ValidationResultEvent;
    import mx.validators.ValidationResult;
    import mx.validators.Validator;

    public class PasswordValidator extends Validator {
        // Define Array for the return value of doValidation().
        private var results:Array;
        
        private var confirmValidator:Validator = new Validator();

        public function PasswordValidator() {
            super();
        }

        private static const MISMATCH:String = "mismatch";

        private var _confirmationSource:Object;
        private var _confirmationProperty:String;

        public var maxLength:int = 16;
        public var minLength:int = 6;
        
        public var passwordRegex:RegExp = /^.{6,16}$/;

        override protected function doValidation(value:Object):Array {
           var results:Array = [];
           var pwd:String = value.password as String;
           if (pwd.length > maxLength || pwd.length < minLength) {
              results.push(new ValidationResult(true, null, "", "密码长度必须大于" + minLength + "小于" + maxLength));
           }
           
           if (!isValidPassword(value.password)) {
              results.push(new ValidationResult(true, null, "", "密码格式不符合规范"));
           }
           
           if (confirmationSource && confirmationProperty) {
              if (value.password != value.confirmation) {
                 if (value.confirmation != null && value.confirmation.length > 0) {
                    var confirmEvent:ValidationResultEvent = new ValidationResultEvent(ValidationResultEvent.INVALID);
                    confirmEvent.results = [new ValidationResult(true, null, "", "确认密码不正确")];
                    confirmValidator.dispatchEvent(confirmEvent);
                 }
                 results.push(new ValidationResult(true, null, "", MISMATCH));
              } else {
                 confirmValidator.dispatchEvent(new ValidationResultEvent(ValidationResultEvent.VALID));
              }
           }
           
           return results;
        }

        /**
         * override this function to make it invoke validationProcessor but not original processValidation
         */
        override public function validate(value:Object = null,
                                          suppressEvents:Boolean = false):ValidationResultEvent {
           if (value == null) {
              value = getValueFromSource();  
           }

           if (isRealValue(value) || required) {
              // Validate if the target is required or our value is non-null.
              return validationProcessor(value, suppressEvents);
           } 
           else {
              // We assume if value is null and required is false that
              // validation was successful.
              var resultEvent:ValidationResultEvent = new ValidationResultEvent(ValidationResultEvent.VALID);
              if (!suppressEvents && enabled) {
                 dispatchEvent(resultEvent);
              }
              return resultEvent; 
           }
        }

        /**
         * this method is used to handle special error message
         */
        private function validationProcessor(value:Object,
                                             suppressEvents:Boolean):ValidationResultEvent {
           var resultEvent:ValidationResultEvent;

           if (enabled) {
              var errorResults:Array = doValidation(value);
              resultEvent = handleResults(errorResults);
           } 
           else {
              suppressEvents = true; // Don't send any events
           }

           if (!suppressEvents) {
              var results:Array = resultEvent.results;
              dispatchEvent(resultEvent);

              //filter error results, to find out whether mismatch message in it
              if (results) {
                 var filteredResults:Array = [];
                 for each (var result:ValidationResult in results) {
                    if (result.errorMessage != MISMATCH) {
                       filteredResults.push(result);
                    }
                 }
                 //if the MESMATCH error is the only error, donot show error string in the source 
                 var sourceTarget:UIComponent = source as UIComponent;
                 if (filteredResults.length == 0 && sourceTarget != null) {
                    sourceTarget.errorString = null;
                    sourceTarget.dispatchEvent(new FlexEvent(FlexEvent.VALID));
                 }
              }
           }

           return resultEvent;
        }

        /**
         *  @private
         *  Grabs the data for the confirmation password from its different sources
         *  if its there and bundles it to be processed by the doValidation routine.
         */
        override protected function getValueFromSource():Object {
            var value:Object = {};

            value.password = super.getValueFromSource();

            if (confirmationSource && confirmationProperty) {
                value.confirmation = confirmationSource[confirmationProperty];
            }

            return value;
        }
        
        /**
         * overrided setter for triggerEvent, to update triggerEvent to confirm source
         */
        override public function set triggerEvent(value:String):void {
           if (triggerEvent == value) {
              return;
           }

           //clear litener on confirm source
           if (_confirmationSource) {
              (_confirmationSource as IEventDispatcher).removeEventListener(triggerEvent, confirmTriggerHandler);
           }

           super.triggerEvent = value;
           //update event listener on confirm source
           if (_confirmationSource) {
              (_confirmationSource as IEventDispatcher).addEventListener(triggerEvent, confirmTriggerHandler);
           }
        }

        public function set confirmationSource(confirmationSource:Object):void {
           //clean event listener
           if (_confirmationSource) {
              (_confirmationSource as IEventDispatcher).removeEventListener(triggerEvent, confirmTriggerHandler);
           }
            this._confirmationSource = confirmationSource;
            //set confirm source to confirm validator, which is used to send INVALID and VALID event to confirm source
            confirmValidator.source = _confirmationSource;

            //add event listener for new confirm source
            if (_confirmationSource) {
               (_confirmationSource as IEventDispatcher).addEventListener(triggerEvent, confirmTriggerHandler);
            }
        }

        private function confirmTriggerHandler(event:Event):void {
           validate();
           if (confirmationSource && confirmationProperty && confirmationSource[confirmationProperty].length == 0) {
              confirmValidator.validate();
           }
        }

        public function get confirmationSource():Object {
            return _confirmationSource;
        }

        public function set confirmationProperty(confirmationProperty:String):void {
            this._confirmationProperty = confirmationProperty;
            confirmValidator.property = _confirmationProperty;
        }

        public function get confirmationProperty():String {
            return _confirmationProperty;
        }

        /**
         *  @public
         *  Use Regular Expression to validate the Password
         *  Only accept all characters which no more than 16 characters
         */
        public function isValidPassword(password:String):Boolean {
           if (passwordRegex) {
              return passwordRegex.test(password);
           } else {
              return true;
           }
        }
    }
}
